using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ICSharpCode.TextEditor.Formatting
{
    public class JavascriptFormatStrategy : IFormattingStrategy
    {
        private class JSBeautifyOptions
        {
            public int? indent_size { get; set; }

            public char? indent_char { get; set; }

            public int? indent_level { get; set; }

            public bool? preserve_newlines { get; set; }
        }

        private class JSBeautify
        {
            private StringBuilder output;

            private string indent_string;

            private int indent_level;

            private string token_text;

            private Stack<string> modes;

            private string current_mode;

            private int opt_indent_size;

            private char opt_indent_char;

            private int opt_indent_level;

            private bool opt_preserve_newlines;

            private bool if_line_flag;

            private bool do_block_just_closed;

            private string input;

            private string whitespace;

            private string wordchar;

            private int parser_pos;

            private string last_type;

            private string last_text;

            private string digits;

            private string[] punct;

            private string prefix;

            private string last_word;

            private bool var_line;

            private bool var_line_tainted;

            private string[] line_starters;

            private bool in_case;

            private bool add_script_tags;

            private string token_type;

            private void trim_output()
            {
                while (output.Length > 0 && (output[output.Length - 1] == ' ' || output[output.Length - 1].ToString() == indent_string))
                {
                    output.Remove(output.Length - 1, 1);
                }
            }

            private void print_newline(bool? ignore_repeated)
            {
                ignore_repeated = ignore_repeated ?? true;
                if_line_flag = false;
                trim_output();
                if (output.Length != 0)
                {
                    if (output[output.Length - 1] != '\n' || !ignore_repeated.Value)
                    {
                        output.Append(Environment.NewLine);
                    }
                    for (int i = 0; i < indent_level; i++)
                    {
                        output.Append(indent_string);
                    }
                }
            }

            private void print_space()
            {
                string text = " ";
                if (output.Length > 0)
                {
                    text = output[output.Length - 1].ToString();
                }
                if (text != " " && text != "\n" && text != indent_string)
                {
                    output.Append(' ');
                }
            }

            private void print_token()
            {
                output.Append(token_text);
            }

            private void indent()
            {
                indent_level++;
            }

            private void unindent()
            {
                if (indent_level > 0)
                {
                    indent_level--;
                }
            }

            private void remove_indent()
            {
                if (output.Length > 0 && output[output.Length - 1].ToString() == indent_string)
                {
                    output.Remove(output.Length - 1, 1);
                }
            }

            private void set_mode(string mode)
            {
                modes.Push(current_mode);
                current_mode = mode;
            }

            private void restore_mode()
            {
                do_block_just_closed = current_mode == "DO_BLOCK";
                current_mode = modes.Pop();
            }

            private bool in_array(object what, ArrayList arr)
            {
                return arr.Contains(what);
            }

            private bool is_ternary_op()
            {
                int num = 0;
                int num2 = 0;
                for (int num3 = output.Length - 1; num3 >= 0; num3--)
                {
                    switch (output[num3])
                    {
                        case ':':
                            if (num == 0)
                            {
                                num2++;
                            }
                            break;
                        case '?':
                            if (num == 0)
                            {
                                if (num2 == 0)
                                {
                                    return true;
                                }
                                num2--;
                            }
                            break;
                        case '{':
                            if (num == 0)
                            {
                                return false;
                            }
                            num--;
                            break;
                        case '(':
                        case '[':
                            num--;
                            break;
                        case ')':
                        case ']':
                        case '}':
                            num++;
                            break;
                    }
                }
                return false;
            }

            private string[] get_next_token(ref int parser_pos)
            {
                int num = 0;
                if (parser_pos >= input.Length)
                {
                    return new string[2] { "", "TK_EOF" };
                }
                string text = input[parser_pos].ToString();
                parser_pos++;
                while (whitespace.Contains(text))
                {
                    if (parser_pos >= input.Length)
                    {
                        return new string[2] { "", "TK_EOF" };
                    }
                    if (text == "\n")
                    {
                        num++;
                    }
                    text = input[parser_pos].ToString();
                    parser_pos++;
                }
                bool flag = false;
                if (opt_preserve_newlines)
                {
                    if (num > 1)
                    {
                        for (int i = 0; i < 2; i++)
                        {
                            print_newline(i == 0);
                        }
                    }
                    flag = num == 1;
                }
                if (wordchar.Contains(text))
                {
                    if (parser_pos < input.Length)
                    {
                        while (wordchar.Contains(input[parser_pos]))
                        {
                            text += input[parser_pos];
                            parser_pos++;
                            if (parser_pos == input.Length)
                            {
                                break;
                            }
                        }
                    }
                    if (parser_pos != input.Length && Regex.IsMatch(text, "^[0-9]+[Ee]$") && (input[parser_pos] == '-' || input[parser_pos] == '+'))
                    {
                        char c = input[parser_pos];
                        parser_pos++;
                        string[] array = get_next_token(ref parser_pos);
                        text = text + c + array[0];
                        return new string[2] { text, "TK_WORD" };
                    }
                    if (text == "in")
                    {
                        return new string[2] { text, "TK_OPERATOR" };
                    }
                    if (flag && last_type != "TK_OPERATOR" && !if_line_flag)
                    {
                        print_newline(null);
                    }
                    return new string[2] { text, "TK_WORD" };
                }
                if (text == "(" || text == "[")
                {
                    return new string[2] { text, "TK_START_EXPR" };
                }
                if (text == ")" || text == "]")
                {
                    return new string[2] { text, "TK_END_EXPR" };
                }
                switch (text)
                {
                    case "{":
                        return new string[2] { text, "TK_START_BLOCK" };
                    case "}":
                        return new string[2] { text, "TK_END_BLOCK" };
                    case ";":
                        return new string[2] { text, "TK_SEMICOLON" };
                    case "/":
                        {
                            string text2 = "";
                            if (input[parser_pos] == '*')
                            {
                                parser_pos++;
                                if (parser_pos < input.Length)
                                {
                                    while (input[parser_pos] != '*' || input[parser_pos + 1] <= '\0' || input[parser_pos + 1] != '/' || parser_pos >= input.Length)
                                    {
                                        text2 += input[parser_pos];
                                        parser_pos++;
                                        if (parser_pos >= input.Length)
                                        {
                                            break;
                                        }
                                    }
                                }
                                parser_pos += 2;
                                return new string[2]
                                {
                            "/*" + text2 + "*/",
                            "TK_BLOCK_COMMENT"
                                };
                            }
                            if (input[parser_pos] != '/')
                            {
                                break;
                            }
                            text2 = text;
                            while (input[parser_pos] != '\r' && input[parser_pos] != '\n')
                            {
                                text2 += input[parser_pos];
                                parser_pos++;
                                if (parser_pos >= input.Length)
                                {
                                    break;
                                }
                            }
                            parser_pos++;
                            if (flag)
                            {
                                print_newline(null);
                            }
                            return new string[2] { text2, "TK_COMMENT" };
                        }
                }
                switch (text)
                {
                    case "/":
                        if ((!(last_type == "TK_WORD") || !(last_text == "return")) && !(last_type == "TK_START_EXPR") && !(last_type == "TK_START_BLOCK") && !(last_type == "TK_END_BLOCK") && !(last_type == "TK_OPERATOR") && !(last_type == "TK_EOF") && !(last_type == "TK_SEMICOLON"))
                        {
                            break;
                        }
                        goto case "'";
                    case "'":
                    case "\"":
                        {
                            string text3 = text;
                            bool flag2 = false;
                            string text4 = text;
                            if (parser_pos < input.Length)
                            {
                                if (text3 == "/")
                                {
                                    bool flag3 = false;
                                    while (flag2 || flag3 || input[parser_pos].ToString() != text3)
                                    {
                                        text4 += input[parser_pos];
                                        if (!flag2)
                                        {
                                            flag2 = input[parser_pos] == '\\';
                                            if (input[parser_pos] == '[')
                                            {
                                                flag3 = true;
                                            }
                                            else if (input[parser_pos] == ']')
                                            {
                                                flag3 = false;
                                            }
                                        }
                                        else
                                        {
                                            flag2 = false;
                                        }
                                        parser_pos++;
                                        if (parser_pos >= input.Length)
                                        {
                                            return new string[2] { text4, "TK_STRING" };
                                        }
                                    }
                                }
                                else
                                {
                                    while (flag2 || input[parser_pos].ToString() != text3)
                                    {
                                        text4 += input[parser_pos];
                                        flag2 = !flag2 && input[parser_pos] == '\\';
                                        parser_pos++;
                                        if (parser_pos >= input.Length)
                                        {
                                            return new string[2] { text4, "TK_STRING" };
                                        }
                                    }
                                }
                            }
                            parser_pos++;
                            text4 += text3;
                            if (text3 == "/")
                            {
                                while (parser_pos < input.Length && wordchar.Contains(input[parser_pos]))
                                {
                                    text4 += input[parser_pos];
                                    parser_pos++;
                                }
                            }
                            return new string[2] { text4, "TK_STRING" };
                        }
                }
                if (text == "#")
                {
                    string text5 = "#";
                    if (parser_pos < input.Length && digits.Contains(input[parser_pos]))
                    {
                        do
                        {
                            text = input[parser_pos].ToString();
                            text5 += text;
                            parser_pos++;
                        }
                        while (parser_pos < input.Length && text != "#" && text != "=");
                        if (text == "#")
                        {
                            return new string[2] { text5, "TK_WORD" };
                        }
                        return new string[2] { text5, "TK_OPERATOR" };
                    }
                }
                if (text == "<" && input.Substring(parser_pos - 1, 3) == "<!--")
                {
                    parser_pos += 3;
                    return new string[2] { "<!--", "TK_COMMENT" };
                }
                if (text == "-" && input.Substring(parser_pos - 1, 2) == "-->")
                {
                    parser_pos += 2;
                    if (flag)
                    {
                        print_newline(null);
                    }
                    return new string[2] { "-->", "TK_COMMENT" };
                }
                if (punct.Contains(text))
                {
                    while (parser_pos < input.Length && punct.Contains(text + input[parser_pos]))
                    {
                        text += input[parser_pos];
                        parser_pos++;
                        if (parser_pos >= input.Length)
                        {
                            break;
                        }
                    }
                    return new string[2] { text, "TK_OPERATOR" };
                }
                return new string[2] { text, "TK_UNKNOWN" };
            }

            public string GetResult()
            {
                if (add_script_tags)
                {
                    output.AppendLine().AppendLine("</script>");
                }
                return output.ToString();
            }

            public JSBeautify(string js_source_text, JSBeautifyOptions options)
            {
                opt_indent_size = options.indent_size ?? 4;
                opt_indent_char = options.indent_char ?? ' ';
                opt_indent_level = options.indent_level ?? 0;
                opt_preserve_newlines = options.preserve_newlines ?? true;
                output = new StringBuilder();
                modes = new Stack<string>();
                for (indent_string = ""; opt_indent_size > 0; opt_indent_size--)
                {
                    indent_string += opt_indent_char;
                }
                indent_level = opt_indent_level;
                input = js_source_text.Replace("<script type=\"text/javascript\">", "").Replace("</script>", "");
                if (input.Length != js_source_text.Length)
                {
                    output.AppendLine("<script type=\"text/javascript\">");
                    add_script_tags = true;
                }
                last_word = "";
                last_type = "TK_START_EXPR";
                last_text = "";
                do_block_just_closed = false;
                var_line = false;
                var_line_tainted = false;
                whitespace = "\n\r\t ";
                wordchar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$";
                digits = "0123456789";
                punct = "+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::".Split(' ');
                line_starters = "continue,try,throw,return,var,if,switch,case,default,for,while,break,function".Split(',');
                current_mode = "BLOCK";
                modes.Push(current_mode);
                parser_pos = 0;
                in_case = false;
                while (true)
                {
                    string[] array = get_next_token(ref parser_pos);
                    token_text = array[0];
                    token_type = array[1];
                    if (token_type == "TK_EOF")
                    {
                        break;
                    }
                    switch (token_type)
                    {
                        case "TK_START_EXPR":
                            var_line = false;
                            set_mode("EXPRESSION");
                            if (last_text == ";" || last_type == "TK_START_BLOCK")
                            {
                                print_newline(null);
                            }
                            else if (!(last_type == "TK_END_EXPR") && !(last_type == "TK_START_EXPR"))
                            {
                                if (last_type != "TK_WORD" && last_type != "TK_OPERATOR")
                                {
                                    print_space();
                                }
                                else if (line_starters.Contains(last_word))
                                {
                                    print_space();
                                }
                            }
                            print_token();
                            break;
                        case "TK_END_EXPR":
                            print_token();
                            restore_mode();
                            break;
                        case "TK_START_BLOCK":
                            if (last_word == "do")
                            {
                                set_mode("DO_BLOCK");
                            }
                            else
                            {
                                set_mode("BLOCK");
                            }
                            if (last_type != "TK_OPERATOR" && last_type != "TK_START_EXPR")
                            {
                                if (last_type == "TK_START_BLOCK")
                                {
                                    print_newline(null);
                                }
                                else
                                {
                                    print_space();
                                }
                            }
                            print_token();
                            indent();
                            break;
                        case "TK_END_BLOCK":
                            if (last_type == "TK_START_BLOCK")
                            {
                                trim_output();
                                unindent();
                            }
                            else
                            {
                                unindent();
                                print_newline(null);
                            }
                            print_token();
                            restore_mode();
                            break;
                        case "TK_WORD":
                            if (do_block_just_closed)
                            {
                                print_space();
                                print_token();
                                print_space();
                                do_block_just_closed = false;
                                break;
                            }
                            if (token_text == "case" || token_text == "default")
                            {
                                if (last_text == ":")
                                {
                                    remove_indent();
                                }
                                else
                                {
                                    unindent();
                                    print_newline(null);
                                    indent();
                                }
                                print_token();
                                in_case = true;
                                break;
                            }
                            prefix = "NONE";
                            if (last_type == "TK_END_BLOCK")
                            {
                                if (!new string[3] { "else", "catch", "finally" }.Contains(token_text.ToLower()))
                                {
                                    prefix = "NEWLINE";
                                }
                                else
                                {
                                    prefix = "SPACE";
                                    print_space();
                                }
                            }
                            else if (last_type == "TK_SEMICOLON" && (current_mode == "BLOCK" || current_mode == "DO_BLOCK"))
                            {
                                prefix = "NEWLINE";
                            }
                            else if (last_type == "TK_SEMICOLON" && current_mode == "EXPRESSION")
                            {
                                prefix = "SPACE";
                            }
                            else if (last_type == "TK_STRING")
                            {
                                prefix = "NEWLINE";
                            }
                            else if (last_type == "TK_WORD")
                            {
                                prefix = "SPACE";
                            }
                            else if (last_type == "TK_START_BLOCK")
                            {
                                prefix = "NEWLINE";
                            }
                            else if (last_type == "TK_END_EXPR")
                            {
                                print_space();
                                prefix = "NEWLINE";
                            }
                            if (last_type != "TK_END_BLOCK" && new string[3] { "else", "catch", "finally" }.Contains(token_text.ToLower()))
                            {
                                print_newline(null);
                            }
                            else if (line_starters.Contains(token_text) || prefix == "NEWLINE")
                            {
                                if (last_text == "else")
                                {
                                    print_space();
                                }
                                else if ((!(last_type == "TK_START_EXPR") && !(last_text == "=") && !(last_text == ",")) || !(token_text == "function"))
                                {
                                    if (last_type == "TK_WORD" && (last_text == "return" || last_text == "throw"))
                                    {
                                        print_space();
                                    }
                                    else if (last_type != "TK_END_EXPR")
                                    {
                                        if ((last_type != "TK_START_EXPR" || token_text != "var") && last_text != ":")
                                        {
                                            if (token_text == "if" && last_type == "TK_WORD" && last_word == "else")
                                            {
                                                print_space();
                                            }
                                            else
                                            {
                                                print_newline(null);
                                            }
                                        }
                                    }
                                    else if (line_starters.Contains(token_text) && last_text != ")")
                                    {
                                        print_newline(null);
                                    }
                                }
                            }
                            else if (prefix == "SPACE")
                            {
                                print_space();
                            }
                            print_token();
                            last_word = token_text;
                            if (token_text == "var")
                            {
                                var_line = true;
                                var_line_tainted = false;
                            }
                            if (token_text == "if" || token_text == "else")
                            {
                                if_line_flag = true;
                            }
                            break;
                        case "TK_SEMICOLON":
                            print_token();
                            var_line = false;
                            break;
                        case "TK_STRING":
                            if (last_type == "TK_START_BLOCK" || last_type == "TK_END_BLOCK" || last_type == "TK_SEMICOLON")
                            {
                                print_newline(null);
                            }
                            else if (last_type == "TK_WORD")
                            {
                                print_space();
                            }
                            print_token();
                            break;
                        case "TK_OPERATOR":
                            {
                                bool flag = true;
                                bool flag2 = true;
                                if (var_line && token_text != ",")
                                {
                                    var_line_tainted = true;
                                    if (token_text == ":")
                                    {
                                        var_line = false;
                                    }
                                }
                                if (var_line && token_text == "," && current_mode == "EXPRESSION")
                                {
                                    var_line_tainted = false;
                                }
                                if (token_text == ":" && in_case)
                                {
                                    print_token();
                                    print_newline(null);
                                    in_case = false;
                                    break;
                                }
                                if (token_text == "::")
                                {
                                    print_token();
                                    break;
                                }
                                if (token_text == ",")
                                {
                                    if (var_line)
                                    {
                                        if (var_line_tainted)
                                        {
                                            print_token();
                                            print_newline(null);
                                            var_line_tainted = false;
                                        }
                                        else
                                        {
                                            print_token();
                                            print_space();
                                        }
                                    }
                                    else if (last_type == "TK_END_BLOCK")
                                    {
                                        print_token();
                                        print_newline(null);
                                    }
                                    else if (current_mode == "BLOCK")
                                    {
                                        print_token();
                                        print_newline(null);
                                    }
                                    else
                                    {
                                        print_token();
                                        print_space();
                                    }
                                    break;
                                }
                                if (token_text == "--" || token_text == "++")
                                {
                                    if (last_text == ";")
                                    {
                                        if (current_mode == "BLOCK")
                                        {
                                            print_newline(null);
                                            flag = true;
                                            flag2 = false;
                                        }
                                        else
                                        {
                                            flag = true;
                                            flag2 = false;
                                        }
                                    }
                                    else
                                    {
                                        if (last_text == "{")
                                        {
                                            print_newline(null);
                                        }
                                        flag = false;
                                        flag2 = false;
                                    }
                                }
                                else if ((token_text == "!" || token_text == "+" || token_text == "-") && (last_text == "return" || last_text == "case"))
                                {
                                    flag = true;
                                    flag2 = false;
                                }
                                else if ((token_text == "!" || token_text == "+" || token_text == "-") && last_type == "TK_START_EXPR")
                                {
                                    flag = false;
                                    flag2 = false;
                                }
                                else if (last_type == "TK_OPERATOR")
                                {
                                    flag = false;
                                    flag2 = false;
                                }
                                else if (last_type == "TK_END_EXPR")
                                {
                                    flag = true;
                                    flag2 = true;
                                }
                                else if (token_text == ".")
                                {
                                    flag = false;
                                    flag2 = false;
                                }
                                else if (token_text == ":")
                                {
                                    flag = (is_ternary_op() ? true : false);
                                }
                                if (flag)
                                {
                                    print_space();
                                }
                                print_token();
                                if (flag2)
                                {
                                    print_space();
                                }
                                break;
                            }
                        case "TK_BLOCK_COMMENT":
                            print_newline(null);
                            print_token();
                            print_newline(null);
                            break;
                        case "TK_COMMENT":
                            print_space();
                            print_token();
                            print_newline(null);
                            break;
                        case "TK_UNKNOWN":
                            print_token();
                            break;
                    }
                    last_type = token_type;
                    last_text = token_text;
                }
            }
        }

        public string StringFormat(string content)
        {
            JSBeautifyOptions options = new JSBeautifyOptions();
            string empty = string.Empty;
            try
            {
                return new JSBeautify(content, options).GetResult();
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        public string StringFormat(string content, int Indent)
        {
            JSBeautifyOptions options = new JSBeautifyOptions
            {
                indent_level = Indent
            };
            string empty = string.Empty;
            try
            {
                return new JSBeautify(content, options).GetResult();
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }
}
